package org.rambadger.command.auth;

import com.google.common.base.Optional;
import com.sun.jersey.api.core.HttpContext;
import com.sun.jersey.core.spi.component.ComponentContext;
import com.sun.jersey.core.spi.component.ComponentScope;
import com.sun.jersey.server.impl.inject.AbstractHttpContextInjectable;
import com.sun.jersey.spi.inject.Injectable;
import com.sun.jersey.spi.inject.InjectableProvider;
import com.yammer.dropwizard.auth.Auth;
import com.yammer.dropwizard.auth.AuthenticationException;
import org.mongojack.JacksonDBCollection;
import org.rambadger.command.axon.JCollection;
import org.rambadger.command.providers.ConfigParam;

import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MultivaluedMap;
import java.lang.reflect.Type;

import static javax.ws.rs.core.Response.Status.UNAUTHORIZED;

/**
 * Jersey Provider that attempts to authenticate based on a browser cookie
 */
public class CookieAuthProvider implements InjectableProvider<Auth, Type> {
    private @ConfigParam("authCookieName") String cookieName = null;

    @JCollection(type=AuthToken.class)
    private JacksonDBCollection<AuthToken, String> authTokens = null;
    @JCollection(type=AuthUser.class)
    private JacksonDBCollection<AuthUser, String> users = null;

    private UserTokenAuthenticator authenticator;


    @Override
    public ComponentScope getScope() {
        return ComponentScope.PerRequest;
    }

    @Override
    public Injectable getInjectable(ComponentContext ic, final Auth annotation, Type type) {
        if (authenticator == null) {
            //We have to defer this step due to the collections not being injected yet.
            authenticator = new UserTokenAuthenticator(authTokens, users);
        }

        return new AbstractHttpContextInjectable<AuthUser>() {
            @Override
            public AuthUser getValue(HttpContext c) {
                MultivaluedMap<String, String> cookies = c.getRequest().getCookieNameValueMap();
                Optional<AuthUser> user = Optional.absent();
                if (cookies.containsKey(cookieName)) {
                    String cookie = cookies.getFirst(cookieName);
                    try {
                        user = authenticator.authenticate(cookie);
                    } catch (AuthenticationException e) {
                        //Throw server error instead?
                        user = Optional.absent();
                    }
                }
                if (!user.isPresent() && annotation.required()) {
                    throw new WebApplicationException(UNAUTHORIZED);
                }
                return user.orNull();
            }
        };
    }

}
